#!/usr/bin/env python3.11
# -*- encoding: utf-8 -*-
'''
@File    :   controllers.py
@Time    :   2024/04/12 16:10:10
@Author  :   windstarry 
@Version :   1.0
'''
# here put the import lib
import os
import uuid
from django.conf import settings
from django.db import transaction
from urllib.parse import unquote
from django.http import FileResponse
from ninja_extra import api_controller, route
from ninja import File
from ninja.files import UploadedFile
from ninja_extra.pagination import (
    PageNumberPaginationExtra,
    PaginatedResponseSchema,
    paginate,
)
from .models import File as FileModel
from .schema import FileSchemaOut,FileSchema
from utils.schemas import MessageSchema
from utils.utils import is_allowed_file

@api_controller("/file", tags=["文件管理"])
class FilesController:
    @route.get(
        "/list",
        summary="文件列表",
        response=PaginatedResponseSchema[FileSchemaOut],
        url_name="files_list",
    )
    @paginate(PageNumberPaginationExtra, page_size=10)
    def get_files_list(
        self,
    ):
        return FileModel.objects.all()
    
    @route.post("/upload",summary="上传文件",response=[(201, FileSchemaOut), (400, MessageSchema)])
    def upload_file(self, file: UploadedFile = File(...)):
        """
        上传文件到服务器的媒体目录。
        
        参数:
        - file: UploadedFile对象，代表上传的文件。
        
        返回值:
        - tuple: 包含HTTP状态码和一个字典，字典包含上传结果的代码和消息。成功时代码为0，失败时代码为1。
        """
        # 检查文件类型是否允许
        if not is_allowed_file(file):
            return  400, ({'code': 1, 'message': '不允许上传该类型的文件'})
        # 生成一个唯一的文件名
        new_name = f'{str(uuid.uuid4())}{os.path.splitext(file.name)[1]}'
        # 构建文件保存的完整路径
        file_path = os.path.join(settings.MEDIA_ROOT, new_name)
        # 尝试将文件内容写入磁盘
        try:
            with open(file_path, 'wb') as f:
                for chunk in file.chunks():
                    f.write(chunk)
        except Exception as e:
            # 如果在写入过程中遇到异常，返回错误信息
            return 400, ({'code': 1, 'message': str(e)})
        # 创建文件上传数据库信息
        with transaction.atomic():           
            file_obj = FileModel(
                name=file.name,
                save_name=new_name,
                url=new_name,
                size=file.size,
            )
            file_obj.save()
        # 如果文件成功上传，返回成功状态和新文件名
        return 201, file_obj
    
    @route.post("/download",summary="下载文件")
    def down_file(self, data: FileSchema):
        """
        下载文件。

        参数:
        - data: FileSchema 类型，包含文件URL的信息。

        返回值:
        - 返回一个文件响应对象，用于触发文件下载。
        """
        try:
            # 构造文件完整路径
            file_path = str(settings.BASE_DIR) + unquote(data.url)
            # 打开文件并以附件形式响应下载请求
            r = FileResponse(open(file_path, "rb"), as_attachment=True)
            return r
        except Exception as e:
            # 遇到异常，返回错误信息
            return 400, ({'code': 1, 'message': str(e)})